/*
 * %W% %E%
 *
 * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package javax.naming.ldap;

import javax.naming.*;
import javax.naming.directory.*;

import java.util.Hashtable;

/**
  * This class is the starting context for performing 
  * LDAPv3-style extended operations and controls.
  *<p>
  * See <tt>javax.naming.InitialContext</tt> and 
  * <tt>javax.naming.InitialDirContext</tt> for details on synchronization,
  * and the policy for how an initial context is created.
  *
  * <h4>Request Controls</h4>
  * When you create an initial context (<tt>InitialLdapContext</tt>),
  * you can specify a list of request controls. 
  * These controls will be used as the request controls for any
  * implicit LDAP "bind" operation performed by the context or contexts
  * derived from the context. These are called <em>connection request controls</em>.
  * Use <tt>getConnectControls()</tt> to get a context's connection request
  * controls.
  *<p>
  * The request controls supplied to the initial context constructor
  * are <em>not</em> used as the context request controls 
  * for subsequent context operations such as searches and lookups.
  * Context request controls are set and updated by using
  * <tt>setRequestControls()</tt>.
  *<p>
  * As shown, there can be two different sets of request controls
  * associated with a context: connection request controls and context 
  * request controls.
  * This is required for those applications needing to send critical
  * controls that might not be applicable to both the context operation and
  * any implicit LDAP "bind" operation.
  * A typical user program would do the following:
  *<blockquote><pre>
  * InitialLdapContext lctx = new InitialLdapContext(env, critConnCtls);
  * lctx.setRequestControls(critModCtls);
  * lctx.modifyAttributes(name, mods);
  * Controls[] respCtls =  lctx.getResponseControls();
  *</pre></blockquote>
  * It specifies first the critical controls for creating the initial context
  * (<tt>critConnCtls</tt>), and then sets the context's request controls
  * (<tt>critModCtls</tt>) for the context operation. If for some reason
  * <tt>lctx</tt> needs to reconnect to the server, it will use 
  * <tt>critConnCtls</tt>. See the <tt>LdapContext</tt> interface for 
  * more discussion about request controls.
  *<p>
  * Service provider implementors should read the "Service Provider" section
  * in the <tt>LdapContext</tt> class description for implementation details.
  * 
  * @author Rosanna Lee
  * @author Scott Seligman
  * @author Vincent Ryan
  * @version %I% %E%
  *
  * @see LdapContext
  * @see javax.naming.InitialContext
  * @see javax.naming.directory.InitialDirContext
  * @see javax.naming.spi.NamingManager#setInitialContextFactoryBuilder
  * @since 1.3
  */

public class InitialLdapContext extends InitialDirContext implements LdapContext {
    private static final String 
        BIND_CONTROLS_PROPERTY = "java.naming.ldap.control.connect";

    /**
     * Constructs an initial context using no environment properties or 
     * connection request controls.
     * Equivalent to <tt>new InitialLdapContext(null, null)</tt>.
     *
     * @throws	NamingException if a naming exception is encountered
     */
    public InitialLdapContext() throws NamingException {
	super(null);
    }

    /**
     * Constructs an initial context
     * using environment properties and connection request controls.
     * See <tt>javax.naming.InitialContext</tt> for a discussion of
     * environment properties.
     *
     * <p> This constructor will not modify its parameters or
     * save references to them, but may save a clone or copy.
     *
     * <p> <tt>connCtls</tt> is used as the underlying context instance's
     * connection request controls.  See the class description
     * for details.
     *
     * @param environment
     *		environment used to create the initial DirContext.
     *		Null indicates an empty environment.
     * @param connCtls
     *		connection request controls for the initial context.
     *		If null, no connection request controls are used.
     *
     * @throws	NamingException if a naming exception is encountered
     *
     * @see #reconnect
     * @see LdapContext#reconnect
     */
    public InitialLdapContext(Hashtable<?,?> environment,
			      Control[] connCtls) 
	    throws NamingException {
	super(true); // don't initialize yet

	// Clone environment since caller owns it.
	Hashtable env = (environment == null)
	    ? new Hashtable(11)
	    : (Hashtable)environment.clone();

	// Put connect controls into environment.  Copy them first since
	// caller owns the array.
	if (connCtls != null) {
	    Control[] copy = new Control[connCtls.length];
	    System.arraycopy(connCtls, 0, copy, 0, connCtls.length);
	    env.put(BIND_CONTROLS_PROPERTY, copy);
	}
	// set version to LDAPv3
	env.put("java.naming.ldap.version", "3");

	// Initialize with updated environment
	init(env);
    }

    /**
     * Retrieves the initial LDAP context.
     * 
     * @return The non-null cached initial context.
     * @exception NotContextException If the initial context is not an
     * instance of <tt>LdapContext</tt>.
     * @exception NamingException If a naming exception was encountered.
     */
    private LdapContext getDefaultLdapInitCtx() throws NamingException{
	Context answer = getDefaultInitCtx();

	if (!(answer instanceof LdapContext)) {
	    if (answer == null) {
		throw new NoInitialContextException();
	    } else {
		throw new NotContextException(
		    "Not an instance of LdapContext");
	    }
	}
	return (LdapContext)answer;
    }

// LdapContext methods
// Most Javadoc is deferred to the LdapContext interface.

    public ExtendedResponse extendedOperation(ExtendedRequest request)
	    throws NamingException {
	return getDefaultLdapInitCtx().extendedOperation(request);
    }

    public LdapContext newInstance(Control[] reqCtls)
	throws NamingException {
	    return getDefaultLdapInitCtx().newInstance(reqCtls);
    }

    public void reconnect(Control[] connCtls) throws NamingException {
	getDefaultLdapInitCtx().reconnect(connCtls);
    }

    public Control[] getConnectControls() throws NamingException {
	return getDefaultLdapInitCtx().getConnectControls();
    }

    public void setRequestControls(Control[] requestControls)
	throws NamingException {
	    getDefaultLdapInitCtx().setRequestControls(requestControls);
    }

    public Control[] getRequestControls() throws NamingException {
	return getDefaultLdapInitCtx().getRequestControls();
    }

    public Control[] getResponseControls() throws NamingException {
	return getDefaultLdapInitCtx().getResponseControls();
    }
}
